Перейти к основному содержимому

5.01. Типы данных в JS

Разработчику Архитектору

Типы данных в JS

Автоматические преобразования (coercion)
Объекты как ссылочные типы


Переменные и константы в процессе использования, получают какое-то значение. Когда мы указываем, что у нас будет возраст (age), мы понимаем, что это будет какое-то число в диапазоне от 0 до 150, к примеру. А для имени (name) будет уже довольно обширный набор символов с разным количеством – от одного до пятидесяти (а может, и больше?). И для устройства это важно, определить участок памяти для небольшого значения (age) или большого (name) – и это определяется типом данных, который даёт системе указание выделить нужный объем ресурсов.

★ JavaScript – язык с динамической типизацией (тип переменной определяется автоматически), поэтому можно просто объявить переменную, не указывая, какого типа она будет, и система сама поймёт, с каким типом работает. Мы просто можем написать «пусть будет x» - «let x», и когда будет x=10, динамическая типизация позволит определить, что тут хватит Number, если же x=”Какой-то текст”, то небольшим размером Number не обойтись, и тип будет указан как String.

Типы в JS бывают примитивными (хранят одно значение) и ссылочными (хранят сложные структуры).

Примитивные типы

ТипОписаниеПример
NumberЧисла (целые и дробные)let age = 25;
StringСтроки (текст)let name = "Анна";
BooleanЛогический (true / false)let isStudent = true;
UndefinedЗначение не присвоеноlet x;
NullПустое значениеlet y = null;
SymbolУникальный идентификаторconst id = Symbol("id");

Как можно понять, примитивные используются для простых данных, когда это какое-то имя, число, текст, или просто «флажок» вроде «истина» или «ложь».

  1. Число (Number). Тип Number представляет как целые, так и вещественные числа. Внутренне используется формат IEEE 754 (64-битное число с плавающей точкой). Поддерживает специальные значения: Infinity, -Infinity, NaN.

Простой пример:

let age = 30;

Сложный пример:

const totalPrice = items.reduce((sum, item) => sum + (item.price * item.quantity), 0)
.toFixed(2);
const finalPrice = parseFloat(totalPrice) + (hasDiscount ? -totalPrice * 0.1 : 0);

Здесь значение переменной finalPrice формируется через цепочку операций: агрегация массива объектов (reduce), округление до двух знаков после запятой (toFixed — возвращает строку), преобразование строки обратно в число (parseFloat) и условная скидка. Результат — число, но его получение требует нескольких этапов обработки.

  1. Булево (Boolean). Принимает два значения: true и false. Также может быть получен неявно через приведение типов (например, !!"text" → true).

Простой пример:

let isActive = true;

Сложный пример:

const isEligible = user.age >= 18 && 
user.hasVerifiedEmail &&
(user.permissions.includes('read') || user.role === 'admin');

Переменная isEligible принимает булево значение, вычисленное на основе нескольких условий: возраст, статус верификации и права доступа. Это типичный случай использования логических выражений для контроля доступа.

  1. Строка (String). Тип String представляет собой последовательность символов UTF-16. Может создаваться как строковый литерал или через конструктор String(). Простой пример:
let name = "Тимур";

Сложный пример:

const greeting = `Добро пожаловать, ${user.name || 'Гость'}!
Вы вошли ${new Date().toLocaleDateString('ru-RU')} в ${new Date().getHours()}:${String(new Date().getMinutes()).padStart(2, '0')}.
Ваш баланс: ${(user.balance ?? 0).toFixed(2)} ₽.`;

Выше используется шаблонная строка с интерполяцией значений из объекта, операторами нулевого слияния (??) и логического ИЛИ (||), форматированием времени и числовым округлением. Результат — многострочная строка, собранная динамически.

Отдельно важно выделить symbol - в JS они особые, и не относятся к «тексту» вроде строки. Это ярлык, который можно использовать как ключ для свойств объекта, как наклейка на шкаф. Каждый Symbol — уникален, даже если у них одинаковое описание. Даже если два символа называются "id", они не равны. Это как два разных браслета с одинаковой гравировкой — выглядят одинаково, но физически разные.

Представим наглядно.

У нас есть объект user, и мы ему решили добавить свойство, сразу в формате строки:

user.id = "12345";

И в дальнейшем, эту переменную id можно перезаписать, заменив значение - если кто-то укажет user.id = значение, то наше изначальное значение будет перезаписано:

user.id = 23456;

И если мы не хотим, чтобы другие скрипты перезаписали случайно наш код, можем использовать Symbol:

user.id = Symbol("id");

user[id] = 12345;

Теперь наш id не перезапишется, потому что не знают о том, что есть id как Symbol. Символы невидимы в циклах, и не покажутся в переборе. Symbol — это скрытое свойство. Его нельзя автоматически превратить в строку при преобразовании типов.

Но в ряде случаев, можно ссылаться к каким-то сложным объектам, как мы ранее уже упоминали – это ссылочные типы.

Ссылочные типы

ТипОписаниеПример
ObjectОбъект (ключ: значение). Кстати, JSON это Java Script Object Notation, так что это, фактически, тот же синтаксис.const user = { name: "Анна" };
ArrayМассив (список значений)const nums = [1, 2, 3];
FunctionФункцияfunction greet() { ... }

Объект имеет какие-то свойства и значения, допустим name – свойство объекта user. Объект, как мы ранее выяснили, может иметь и свои функции – это будут методы.

Объект — это коллекция пар «ключ-значение». Является ссылочным типом. Все не-примитивы в JS являются объектами (включая массивы и функции).

Простой пример:

let person = { name: "Иван", age: 28 };

Сложный пример:

const config = Object.freeze({
apiUrl: process.env.API_URL || 'https://api.example.com',
timeout: parseInt(process.env.TIMEOUT || '5000', 10),
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${authToken}`
},
retryCount: maxRetries > 0 ? Math.min(maxRetries, 5) : 0
});

Выше в примере мы видим, как создаётся объект конфигурации с использованием переменных окружения, преобразованием типов, условной логикой и вложенностью. Затем он замораживается (Object.freeze) для предотвращения изменений — типичная практика в продвинутых приложениях.

Объекты могут быть базовыми, DOM, пользовательскими и глобальным.

Глобальный объект - это объект, который существует в любом окружении выполнения JavaScript и доступен всегда, везде. Все глобальные переменные, функции, встроенные константы на самом деле являются свойствами этого объекта.

Какой именно объект является глобальным, зависит от окружения:

ОкружениеГлобальный объект
Браузерwindow
Node.jsglobal
Современный JS (в любом окружении)globalThis

Как это работает? Такой глобальный объект содержит встроенные глобальные переменные, функции и объекты:

Свойства:

  • Infinity - бесконечность;
  • NaN - «не число»;
  • undefined - значение undefined;
  • globalThis - ссылка на самого себя.

Встроенные функции:

  • isNaN() - проверяет, является ли значение NaN;
  • isFinite() - проверяет, является ли значение конечным числом;
  • parseFloat(), parseInt() - парсинг чисел;
  • encodeURI(), decodeURI() - кодировка URL;
  • eval() - выполнение строки как кода.

Встроенные конструкторы (которые на самом деле - функции):

  • Object - объект;
  • Array - массив;
  • String - строка;
  • Number - число;
  • Boolean - булево значение;
  • Function - функция;
  • Date - дата;
  • RegExp - регулярное выражение;
  • Symbol - уникальный идентификатор;
  • Error, TypeError, SyntaxError - ошибки.

Встроенные объекты:

  • Math - математические функции;
  • JSON - работа с JSON;
  • Promise - промисы (позже их изучим);
  • console - консоль.

Если объявить переменную без var, let, const в глобальной области - она станет свойством глобального объекта. То есть не пишите вроде x = 42, если не хотите создавать глобальное свойство - добавляйте ключевое слово let или const.

Важно - let, const, class не создают свойства в глобальном объекте, а var создаёт, как и вышеприведенный пример с x = 42.

Массив же используется как набор данных, когда мы хотим расширить набор данных. К примеру, nums – набор чисел, включающий три значения – 1, 2 и 3. Каждый из этих элементов массива имеет индекс, что позволяет обращаться к конкретному значению, допустим, 2.

Массив (Array) — упорядоченная коллекция элементов. Является подтипом объекта, но имеет специфические методы (map, filter, reduce и т.д.).

Простой пример:

let numbers = [1, 2, 3];

Сложный пример:

const filteredUsers = users
.filter(user => user.active)
.map(user => ({
...user,
fullName: `${user.firstName} ${user.lastName}`.trim(),
roleLabel: roleMap[user.role] || 'Неизвестно'
}))
.sort((a, b) => a.fullName.localeCompare(b.fullName));

Здесь выполняется фильтрация активных пользователей, маппинг с расширением данных (включая объединение полей и использование внешнего маппинга ролей), затем сортировка по алфавиту. Результат — новый массив объектов с расширенной информацией.

Функция – это та сама функция, которые мы изучили ранее. Она тоже представляет собой тип данных.

Функция (Function) — это объект первого класса. Может быть передана как аргумент, возвращена из другой функции, храниться в переменной.

Простой пример:

function greet(name) {
return `Привет, ${name}!`;
}

Сложный пример:

const createValidator = (rules) => (data) => {
const errors = [];
for (const [field, rule] of Object.entries(rules)) {
if (!rule(data[field])) {
errors.push(`Поле "${field}" не прошло валидацию.`);
}
}
return errors.length === 0 ? null : errors;
};

const validateUser = createValidator({
email: (v) => /\S+@\S+\.\S+/.test(v),
age: (v) => v >= 18 && v < 120
});

Здесь реализован паттерн каррирования: функция высшего порядка возвращает другую функцию. createValidator абстрагирует логику валидации, позволяя генерировать конкретные валидаторы. Это мощный инструмент функционального программирования.